package com.bright.hxj.cwduijie.service;

import com.bright.ghj.common.util.StringUtil;
import com.bright.hxj.cwduijie.enums.DataApi;
import com.bright.hxj.cwduijie.enums.SJJHBZ;
import com.bright.hxj.cwduijie.manager.DataManager;
import com.bright.hxj.cwduijie.manager.DuijieManager;
import com.bright.hxj.cwduijie.pojo.bo.ApiDataCollection;
import com.bright.hxj.cwduijie.pojo.command.DeleteDataKeysCommand;
import com.bright.hxj.cwduijie.pojo.command.DuijieCommand;
import com.bright.hxj.cwduijie.pojo.command.SaveSendDataResultCommand;
import com.bright.hxj.cwduijie.pojo.command.SaveTaskIdCommand;
import com.bright.hxj.cwduijie.pojo.dto.CheckResultDto;
import com.bright.hxj.cwduijie.pojo.dto.DuijieDataKeysDto;
import com.bright.hxj.cwduijie.pojo.dto.SendDataReturnDto;
import com.bright.hxj.cwduijie.pojo.query.DuijieQuery;
import com.bright.hxj.cwduijie.service.impl.CheckServiceImpl;
import com.bright.hxj.cwduijie.util.CommonUtil;
import com.bright.hxj.cwduijie.util.EntityManagerUtil;
import com.bright.ghj.common.util.ListUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 */
@Component
@Slf4j
public abstract class AbstractApiParentService<T> implements AbstractApiService<T>, InitializingBean {

    @Autowired
    protected CheckServiceImpl checkService;

//    @Autowired v1.6.7 出现循环依赖 dataBusiness类新引用了ApiServiceFactory factory引用了AbstractApiService
//    protected DataBusiness dataBusiness;

    @Autowired
    protected ObjectMapper objectMapper;

//    @Autowired
    protected WebExchangeService webExchangeService;

    @Autowired
    @Qualifier("normal")
    protected WebExchangeService defaultWebExchangeService;

    @Autowired
    protected DuijieManager duijieManager;

    @Autowired
    protected EntityManager entityManager;

    @Autowired
    protected DataManager dataManager;

    // v1.8.1 给webExchangeService设默认值，指定默认使用正式环境的接口参数；这个值允许调用sendData方法时修改
    @Override
    public void afterPropertiesSet() throws Exception {
        this.webExchangeService = defaultWebExchangeService;
    }

    //    public abstract JpaSpecificationExecutor getJpaSpecificationExecutor();


//    public abstract DataApi getDataApi();
//    public abstract Function<T, Integer> getIdFunc();

    /**
     * 是否存在TYSHXYDM这个字段 如果没这个就是ZT_DATA_KEY做账套标识
     * @return
     */
//    public boolean isExistsTYSHXYDMField() {
//        return true;
//    }

    /**
     * 获取所有需要对接数据的账套
     * @return 账套的DATA_KEY列表
     */
    public List<String> getNeedDuijieZtDataKeys(DuijieCommand duijieCommand) {
        // 有统一社会信用代码的用它 没有就用ZT_DATA_KEY
        if (isExistsTYSHXYDMField()) {
            return getNeedDuijieZtDataKeysByCreditCode(duijieCommand);
        }
        return getNeedDuijieZtDataKeysOfZtDataKey(duijieCommand);
    }

    /**
     * group by 字段是ZT_DATA_KEY
     * @return
     */
    protected List<String> getNeedDuijieZtDataKeysOfZtDataKey(DuijieCommand duijieCommand) {
        DataApi dataApi = getDataApi();
        String yearMonthColumnName = getYearMonthColumnName();

        String groupBySql = "select ZT_DATA_KEY from \"" + dataApi.getTableName() + "\" where sjjhbz in (0, 2) ${subSql} group by ZT_DATA_KEY";
        String subSql;
        if (StringUtil.isNotBlank(yearMonthColumnName) && duijieCommand.getYears() != null && duijieCommand.getMonths() != null) {
            subSql = " and " + yearMonthColumnName + "='" + CommonUtil.getSelectedYearMonth(duijieCommand.getYears(), duijieCommand.getMonths()) + "'";
        } else if (dataApi == DataApi.ACC_CODE) {
            // v1.8 科目 单独只加年份
            subSql = " and NF=" + duijieCommand.getYears();
        } else {
            subSql = "";
        }
        groupBySql = StringUtil.replace(groupBySql, "${subSql}", subSql);

        List<Map<String, Object>> maps = EntityManagerUtil.queryForMapList(entityManager, groupBySql, Collections.emptyMap());

        List<String> ztDataKeys = ListUtil.toSingleList(maps, map -> (String) map.get("ZT_DATA_KEY"));
        return ztDataKeys;
        // v1.8 改成都按统一社会信用代码返回（废弃）
//        Set<String> creditCodeByZtDataKeys = dataManager.getCreditCodeByZtDataKeys(new HashSet<>(ztDataKeys));
//        return new ArrayList<>(creditCodeByZtDataKeys);
    }

    /**
     * group by 字段是TYSHXYDM
     * @return
     */
    protected List<String> getNeedDuijieZtDataKeysByCreditCode(DuijieCommand duijieCommand) {
        DataApi dataApi = getDataApi();
        String yearMonthColumnName = getYearMonthColumnName();

        String groupBySql = "select TYSHXYDM from \"" + dataApi.getTableName() + "\" where sjjhbz in (0, 2) ${subSql} group by TYSHXYDM";

        String subSql;
        if (StringUtil.isNotBlank(yearMonthColumnName) && duijieCommand.getYears() != null && duijieCommand.getMonths() != null) {
            subSql = " and " + yearMonthColumnName + "='" + CommonUtil.getSelectedYearMonth(duijieCommand.getYears(), duijieCommand.getMonths()) + "'";
        } else {
            subSql = "";
        }
        groupBySql = StringUtil.replace(groupBySql, "${subSql}", subSql);

        List<Map<String, Object>> maps = EntityManagerUtil.queryForMapList(entityManager, groupBySql, Collections.emptyMap());

        List<String> creditCodes = ListUtil.toSingleList(maps, map -> (String) map.get("TYSHXYDM"));

        // 根据统一社会信用代码转换为ztDataKey
        // v1.8 这里补充传入对接年份 返回的ZT_DATA_KEY会带上年份（除2024）
        Set<String> ztDataKeys = dataManager.getZtDataKeyByCreditCodes(new HashSet<>(creditCodes), duijieCommand.getYears());
        return new ArrayList<>(ztDataKeys);
        // v1.8 改成按统一社会信用代码返回（废弃）
//        return creditCodes;
    }


    public void getDataAndCheckResults(Map<String, CheckResultDto> checkResultMap,
                                                ApiDataCollection apiDataCollection, DuijieQuery duijieQuery) {
        getData(apiDataCollection, duijieQuery);
        checkDataValid(checkResultMap, apiDataCollection);
    }

    protected abstract void getData(ApiDataCollection apiDataCollection, DuijieQuery duijieQuery);
    protected abstract void checkDataValid(Map<String, CheckResultDto> checkResultDtoMap, ApiDataCollection apiDataCollection);

    @Override
    public void sendData(ApiDataCollection apiDataCollection, DuijieQuery duijieQuery, String batchId, WebExchangeService webExchangeService) throws JsonProcessingException {
        // v1.8.1 调用接口的service从自动注入改成参数传入
        this.webExchangeService = webExchangeService;
        sendData(apiDataCollection, duijieQuery, batchId);
    }

    public abstract void sendData(ApiDataCollection apiDataCollection, DuijieQuery duijieQuery, String batchId) throws JsonProcessingException;

    @Override
    public void sendDeleteData(DuijieQuery duijieQuery, String batchId, WebExchangeService webExchangeService) throws JsonProcessingException {
        this.webExchangeService = webExchangeService;
        sendDeleteData(duijieQuery, batchId);
    }

    public abstract void sendDeleteData(DuijieQuery duijieQuery, String batchId) throws JsonProcessingException;

    /**
     * 检查全表是否有重复数据
     * @return
     */
    public CheckResultDto checkDataKeyDuplicate() {
        String groupByFields = getCheckDuplicateGroupByFields();
        DataApi api = getDataApi();
        List<String> messages = Collections.emptyList();

        Long count;
        try {
            count = getCheckDuplicateCount(groupByFields, api);
            if (count > 0) {
                messages = Collections.singletonList(api.getDescription() + "-" + api.getTableName() + "存在" + groupByFields + "重复数据，条数：" + count);
            }

        } catch (Throwable throwable) {
            log.error("checkDataKeyDuplicate " + api.getTableName() + " 执行出错：" + throwable.getMessage());
            throwable.printStackTrace();
        }
        return new CheckResultDto(messages.size() == 0, messages);
    }

    /**
     * 获取检查重复group by的字段 子类不同可以重写
     * @return
     */
    protected String getCheckDuplicateGroupByFields() {
        return "DATA_KEY";
    }

    protected Long getCheckDuplicateCount(String groupByFields, DataApi api) {
        return EntityManagerUtil.queryForCount(entityManager,
                "select ISNULL(SUM(CNT),0) from (select COUNT(*) CNT from \"" + api.getTableName() + "\" where ISNULL(opType,1)=1 group by " + groupByFields + " having COUNT(*)>1) aa",
                Collections.emptyMap());
    }

//    protected void addErrorMsgs(Map<String, CheckResultDto> errorMsgs, CheckResultDto checkSingleDataCommon,
//                                List<Integer> ids) {
//        if (!checkSingleDataCommon.isSuccess()) {
//            String serviceCode = getDataApi().getServiceCode();
//            if (!errorMsgs.containsKey(serviceCode)) {
//                List<String> newMsgList = new ArrayList<>();
//                errorMsgs.put(serviceCode, new CheckResultDto(false, newMsgList));
//            }
//            CheckResultDto checkResultDto = errorMsgs.get(serviceCode);
//
//            checkResultDto.setIds(ids);
//            checkResultDto.getMessages().addAll(checkSingleDataCommon.getMessages());
//        }
//    }

    protected <T> void sendListData(List<T> dataListObject, List<Integer> ids, String batchId, Integer accSet)  throws JsonProcessingException {
        boolean hasData = dataListObject.size() > 0;
        sendData(dataListObject, getDataApi(), ids, batchId, hasData, accSet);
    }

    protected <K, V> void sendMapData(Map<K, V> dataMapObject, DataApi dataApi, List<Integer> ids, String batchId, Integer accSet)  throws JsonProcessingException {
        boolean hasData = dataMapObject.size() > 0;
        sendData(dataMapObject, dataApi, ids, batchId, hasData, accSet);
    }

    public void sendObjectData(Object dataObject, DataApi dataApi, List<Integer> ids, String batchId, Integer accSet)  throws JsonProcessingException {
        // object类型的 如果传null就是没数据
        boolean hasData = dataObject != null;
        sendData(dataObject, dataApi, ids, batchId, hasData, accSet);
    }

    protected void sendData(Object dataObject, DataApi dataApi, List<Integer> ids, String batchId, boolean hasData, Integer accSet) throws JsonProcessingException {
//        String accSetsString = objectMapper.writeValueAsString(object);
//        DataApi dataApi = getDataApi();
        // 这里dataApi有可能是写入接口或删除接口

        SendDataReturnDto accSetResult = null;
        String errorMsg = null;
        String dataStr = null;

        // 判断如果没有数据 就不推送 但要保存到任务表 （20240726 不保存了）
        if (hasData) {
            try {
                dataStr = objectMapper.writeValueAsString(dataObject);
                accSetResult = webExchangeService.sendData(dataStr, dataApi.getServiceCode());
            } catch (Throwable throwable) {
//            throwable.printStackTrace();
                errorMsg = "接口发送时异常：" + throwable.getMessage();
                log.error("接口发送时异常：", throwable);
            }
        }

        // 根据返回结果 回写异步任务id到表里
        int sjjhbz;
        String taskId;
//        if (!hasData) {
//            // 如果没有数据 就是直接成功
//            sjjhbz = SJJHBZ.SUCCESS.getValue();
//            errorMsg = "无该表数据 无需推送";
//            taskId = null;
//        } else
        // 20240726 现在都是一批过了 无需推送不再记录进异步任务表
        if (hasData) {
            if (accSetResult != null && accSetResult.getIsSuccess()) {
                sjjhbz = SJJHBZ.EXECUTING.getValue();
                errorMsg = SJJHBZ.EXECUTING.getDescription();
                taskId = accSetResult.getTaskId();
            } else {
                sjjhbz = SJJHBZ.ERROR.getValue();
                // 如果出错 自己建一个taskId用于区分
                taskId = "error-" + System.currentTimeMillis();
                if (errorMsg == null && accSetResult != null) {
                    errorMsg = "接口返回异常：" + accSetResult.getMessage();
                }
            }
            // 存一条数据进duijie_async
            SaveTaskIdCommand saveTaskIdCommand = new SaveTaskIdCommand();
            saveTaskIdCommand.setTaskId(taskId);
            saveTaskIdCommand.setServiceCode(dataApi.getServiceCode());
            saveTaskIdCommand.setType(dataApi.getDescription() + "-" + dataApi.getTableName());
            saveTaskIdCommand.setErrorMsg(errorMsg);
            saveTaskIdCommand.setSjjhbz(sjjhbz);
            saveTaskIdCommand.setContent(dataStr);
            saveTaskIdCommand.setBatchId(batchId);
            saveTaskIdCommand.setAccSet(accSet);
            duijieManager.saveAsyncTaskId(saveTaskIdCommand);
            // 存到各张数据表的sjjhbz
            SaveSendDataResultCommand command = new SaveSendDataResultCommand();
            command.setDataApi(dataApi);
            command.setSjjhbz(sjjhbz);
            command.setTaskId(taskId);
            command.setIds(ids);
            duijieManager.saveSendDataResult(command);
        }
//        return null;
    }

    // 根据对接年份和月份构造yearMonth 注：允许只传年份和年份月份必传 若没传年份 则月份不起效
    public static String getSelectedYearMonth(DuijieQuery duijieQuery) {
        return CommonUtil.getSelectedYearMonth(duijieQuery.getDuijieYears(), duijieQuery.getDuijieMonths());
    }

    /**
     * 获取本次推送的数据的所有id值
     * @return
     */
    public abstract <OBJ> Function<OBJ, String> getDataKeyFunc();

    /**
     * 保存推送过的data_key
     * @param listData
     * @param getDataKeyFUnc
     */
    protected <OBJ> void saveSendDataKeys(List<OBJ> listData, Function<OBJ, String> getDataKeyFUnc) {
        DataApi dataApi = getDataApi();

        List<String> duijieDataKeys = ListUtil.toSingleList(listData, getDataKeyFUnc);
        List<DuijieDataKeysDto> duijieDataKeysDtos = new ArrayList<>();
        for (String duijieDataKey : duijieDataKeys) {
            duijieDataKeysDtos.add(new DuijieDataKeysDto(null, dataApi.getServiceCode(), dataApi.getTableName(), duijieDataKey,
                    SJJHBZ.WAITING.getValue(), null, new Date(), null));
        }
        duijieManager.saveDuijieDataKeys(duijieDataKeysDtos);
    }

    @Override
    public DataApi getDeleteDataApi() {
        return null;
    }


//    @Override
//    public void sendDeleteData(List<T> needDeleteData, DuijieQuery duijieQuery, String batchId, WebExchangeService webExchangeService) throws JsonProcessingException {
//        if (getDeleteDataApi() != null) {
//            sendNeedDeleteData(batchId, needDeleteData, getDeleteDataApi(), getDataKeyFunc(), getIdFunc(), duijieQuery.getAccSet());
//        }
//    }

    // 删除数据的公共方法
    public <OBJ> void  sendNeedDeleteData(String batchId, List<OBJ> needDeleteObjs, DataApi deleteApi, Function<OBJ, String> getDataKeyFunc,
                                      Function<OBJ, Integer> getIdFunc, Integer accSet) throws JsonProcessingException {
        // 1000条发送一次
        int count = 0;
        while (count < needDeleteObjs.size()) {
            List<OBJ> subListNeedDeleteVouchers = ListUtil.getSubList(needDeleteObjs, count, count + 1000);
            List<String> needDeletedDataKeys = ListUtil.toSingleList(subListNeedDeleteVouchers, getDataKeyFunc)
                    .stream().distinct()
                    .collect(Collectors.toList());
            Map<String, Object> deleteMap = new HashMap<>();
            deleteMap.put("DATA_KEYS", needDeletedDataKeys);
            sendMapData(deleteMap, deleteApi, ListUtil.toSingleList(subListNeedDeleteVouchers, getIdFunc), batchId, accSet);

            saveDeletedDataKeys(deleteApi, needDeletedDataKeys);

            count += 1000;
        }
    }

    protected void saveDeletedDataKeys(DataApi deleteApi, List<String> needDeletedDataKeys) {
        DeleteDataKeysCommand command = new DeleteDataKeysCommand();
        command.setDataKeys(needDeletedDataKeys);
        command.setServiceCode(StringUtil.replace(deleteApi.getServiceCode(), "D", "W")); // 把接口编号前面的D替换成W就是相应的写入接口
        duijieManager.deleteDuijieDataKeys(command);
    }

//    @Override
//    public String getYearMonthColumnName() {
//        return "";
//    }
}
